home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mm / mm-0.90 / exit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-18  |  13.8 KB  |  663 lines

  1. /*
  2.  * Copyright (c) 1986, 1990 by The Trustees of Columbia University in
  3.  * the City of New York.  Permission is granted to any individual or
  4.  * institution to use, copy, or redistribute this software so long as it
  5.  * is not sold for profit, provided this copyright notice is retained.
  6.  */
  7.  
  8. #ifndef lint
  9. static char *rcsid = "$Header: /f/src2/encore.bin/cucca/mm/tarring-it-up/RCS/exit.c,v 2.6 90/10/04 18:24:07 melissa Exp $";
  10. #endif
  11.  
  12. /*
  13.  * exit.c - various ways to exit mm
  14.  */
  15.  
  16. #include "mm.h"
  17. #include "parse.h"
  18. #include "cmds.h"
  19.  
  20. extern int OLD_UMASK;
  21.  
  22. int
  23. cmd_quit (n)
  24. int n;
  25. {
  26.     int i,updfl;
  27.     extern int expunge_on_bye;
  28.  
  29.     confirm ();
  30.     if (mode & MM_SEND) {        /* if sending, quit that mode */
  31.     mode &= ~(MM_SEND|MM_ANSWER);
  32.     return true;
  33.     }
  34.     if (mode & MM_READ) {        /* if reading, quit that mode */
  35.     mode &= ~MM_READ;
  36.     return true;
  37.     }
  38.     if (suspend_on_quit && (n == CMD_QUIT))  {
  39.     suspend(UPD_SAVEMOD);
  40.     }
  41.     else {                /* qquit/bye */
  42.     if (cf != NULL && 
  43.         (!(cf->flags & MF_RDONLY) || (cf->flags & MF_SAVEONLY))) {
  44.         /* if deleted messages, ask about expunging */
  45.         switch (expunge_on_bye) {
  46.         case SET_NO:
  47.         updfl = UPD_SAVEMOD;
  48.         break;
  49.         case SET_YES:
  50.         updfl = UPD_SAVEMOD|UPD_EXPUNGE;
  51.         break;
  52.         default:            /* SET_ASK */
  53.         updfl = UPD_SAVEMOD;
  54.         for (i = 1; i <= cf->count; i++)
  55.             if (cf->msgs[i].flags & M_DELETED) {
  56.             if (yesno ("Expunge deleted messages? ", "yes"))
  57.                 updfl |= UPD_EXPUNGE;
  58.             break;
  59.             }
  60.         break;
  61.         }
  62.         cmtend ();            /* let go of the terminal */
  63.         if (!update (&cf,updfl) && (cf->flags & MF_WRITERR)) {
  64.         fprintf (stderr, "Cannot save file, not quitting.\n");
  65.         fprintf (stderr,
  66. "Use the SHELL or SUSPEND command to temporarily exit MM, free up some\n\
  67. space, and then try again.\n");
  68.         cmtset ();        /* set CCMD up again */
  69.         cmcsb._cmwrp = autowrap_column;    /* put that back */
  70.         return (false);
  71.         }
  72.     }
  73.     else
  74.         cmtend ();            /* let go of the terminal */
  75. #ifdef MDEBUG
  76.     m_done();
  77. #endif /* MDEBUG */
  78. #ifdef USAGEFILE
  79.     usage_stop(USAGEFILE);
  80. #endif
  81.     exit (0);            /* quit entirely */
  82.     }
  83. }
  84.  
  85. int
  86. cmd_exit (n)
  87. int n;
  88. {
  89.     confirm ();
  90.     if (!suspend_on_exit) {
  91.     cmtend ();
  92.     if (cf != NULL)            /* expunge while saving */
  93.         if (!update (&cf,UPD_EXPUNGE|UPD_SAVEMOD) &&
  94.         (cf->flags & MF_WRITERR)) {
  95.         fprintf (stderr, "Cannot save file, not exiting.\n\
  96. Try suspending MM, and making space by deleting some files.\n");
  97.         cmtset ();        /* set CCMD up again */
  98.         cmcsb._cmwrp = autowrap_column;    /* put that back */
  99.         return (false);
  100.         }
  101.  
  102.     done (0);
  103.     }
  104.     else {
  105.     if (mode == MM_TOP_LEVEL) {
  106.         suspend(UPD_EXPUNGE|UPD_SAVEMOD); /* squish on return */
  107.         if (cf != NULL)        /* is it empty? */
  108.         squish (cf);        /* no, free deleted messages */
  109.     }
  110.     else
  111.         suspend(0);            /* same as suspend */
  112.     }
  113. }
  114.  
  115.  
  116. /*
  117.  * cmd_expunge:
  118.  * Write out the mail file without the deleted messages.  Adjust the
  119.  * msgvec structure to remove these messages internally as well.
  120.  */
  121. int
  122. cmd_expunge (n)
  123. int n;
  124. {
  125.     confirm ();
  126.     if (mode & MM_READ)    {        /* read mode? */
  127.     fprintf (stderr,
  128.          "You cannot run expunge while in read mode, try write.\n");
  129.     return;
  130.     }
  131.     if (!check_cf(O_WRONLY))        /* fails if no file or unwriteable */
  132.     return;
  133.     if (!update (&cf,UPD_EXPUNGE|UPD_SAVEMOD)) /* do the expunge */
  134.     return;
  135.     if (cf != nil)            /* is it empty? */
  136.     squish (cf);            /* no, free deleted messages */
  137. }
  138.  
  139. /*
  140.  * cmd_write:
  141.  * Write out the mail file with the deleted messages.
  142.  */
  143. int
  144. cmd_write (n)
  145. int n;
  146. {
  147.     char *savefile;            /* where to write it to */
  148.     char *parse_output_file();
  149.     time_t save_mtime;
  150.  
  151.     noise ("out mail file to");
  152.     if (!check_cf(O_RDONLY))        /* error return if no file */
  153.     return;
  154.     savefile = parse_output_file("confirm with carriage return\n\
  155.   or alternate filename", cf->filename, true);
  156.     confirm ();                /* XXX memory leak */
  157.     if (strcmp (savefile, cf->filename) == 0) { /* same file, save work */
  158.     if ((cf->flags & MF_RDONLY) && !(cf->flags & MF_SAVEONLY))
  159.         cmerr ("cannot save read-only (examined) file under same name\n");
  160.     update (&cf,UPD_ALWAYS);    /* do the write */
  161.     return;
  162.     }
  163.     else {
  164.     char realfile[MAXPATHLEN];
  165.     /* XXX This is bogus */
  166.     strcpy (realfile,cf->filename);    /* remember real filename */
  167.     strcpy (cf->filename, savefile); /* pretend this is the file */
  168.     save_mtime = cf->mtime;
  169.     update (&cf, UPD_ALWAYS|UPD_ALTFILE);
  170.     cf->mtime = save_mtime;        /* shouldn't change for altfile */
  171.     strcpy (cf->filename, realfile); /* put that back */
  172.     }
  173.     (void) free (savefile);
  174.     return;
  175. }
  176.  
  177.  
  178. /*
  179.  * squish:
  180.  * Remove deleted messages from the msgvec structure passed, and
  181.  * squish remaining messages together.
  182.  */
  183. squish (mail)
  184. msgvec *mail;
  185. {
  186.     int oldpos,newpos;            /* to move remaining messages */
  187.     char *text;                /* text to free */
  188.     message *msgs;            /* gonna use it a lot */
  189.  
  190. #ifdef DEBUG
  191.     debug_validate_msgvec("before squish");
  192. #endif
  193.     if ((msgs = mail->msgs) == NULL)    /* easy access! */
  194.     return;                /* empty, nothing to squish */
  195.     for (oldpos = newpos = 1; oldpos <= mail->count; oldpos++) {
  196.     if (msgs[oldpos].flags & M_DELETED) {
  197.         if ((text = msgs[oldpos].text) != NULL)
  198.         free (text);
  199.         if (msgs[oldpos].from != NULL)
  200.             free (msgs[oldpos].from);
  201.         if (msgs[oldpos].hdrsum != NULL)
  202.             free (msgs[oldpos].hdrsum);
  203.         if (msgs[oldpos].keywords != NULL)
  204.             free_keylist(msgs[oldpos].keywords);
  205.     }
  206.     else {
  207.         if (newpos != oldpos)    /* past deleted messages? */
  208.         msgs[newpos] = msgs[oldpos]; /* move it down */
  209.         newpos++;
  210.     }
  211.     }
  212.     mail->current = mail->count = newpos-1; /* didn't write to final pos'n */
  213.     mail->msgs = (message *) realloc (mail->msgs, 
  214.                       (mail->count+1)*sizeof (message));
  215. #ifdef DEBUG
  216.     debug_validate_msgvec("after squish");
  217. #endif
  218. }
  219.  
  220. int
  221. cmd_take (n)
  222. int n;
  223. {
  224.     extern top_level_parser();
  225.  
  226.     cmtake(top_level_parser);
  227.     cmcsb._cmwrp = autowrap_column;    /* put that back! */
  228. }
  229.  
  230. panic (s)
  231. char *s;
  232. {
  233.     printf ("%s: panic: %s\n", progname, s);
  234.     cmtend ();
  235.     abort();
  236. }
  237.  
  238. char *
  239. errstr(err)
  240. {
  241.     static char temp[16];
  242.  
  243.     switch (err) {
  244.       case 0:
  245.     return (char *) "no error";
  246.       case -1:
  247.     err = errno;
  248.       default:
  249.     if ((err < 0) || (err > sys_nerr)) {
  250.         sprintf(temp,"Error %d", err);
  251.         return (char *) temp;
  252.     }
  253.     return (char *) sys_errlist[err];
  254.     }
  255. }
  256.  
  257. int
  258. cmd_push (n)
  259. int n;
  260. {
  261.     confirm ();
  262.     shell (nil);
  263.     return true;
  264. }
  265.  
  266. shell (cmd)
  267. char *cmd;
  268. {
  269.     int pid, status;
  270.     char *getenv (), *cp;
  271.  
  272.     /*
  273.      * put tty back the way we found it
  274.      */
  275.     cmtend ();
  276.  
  277.     /*
  278.      * update the user's file, just in case
  279.      */
  280.     if (cf)
  281.     update(&cf,0);
  282.  
  283.     /*
  284.      * make sure user knows how to get back
  285.      */
  286.     if (!cmd)
  287.     printf("Pushing to subshell, use \"exit\" to return to MM\n");
  288.  
  289.     if (!(cp = getenv ("SHELL")))
  290.     cp = "/bin/sh";
  291.  
  292.     fix_signals_for_fork (true);
  293.  
  294.     pid = vfork ();
  295.     if (pid == 0) {
  296.     umask(OLD_UMASK);
  297.     if (cmd)
  298.         /* one-shot command */
  299.         execl (cp, cp, "-c", cmd, 0);
  300.     else
  301.         /* interactive shell */
  302.         execl (cp, cp, 0);
  303.     perror (cp);
  304.     _exit (1);
  305.     }
  306.     if (pid > 0)
  307.     status = wait_for_process (pid);
  308.     else
  309.     perror ("mm: fork");
  310.  
  311.     fix_signals_for_fork (false);    /* restore SIGCHLD, tty pgrp */
  312.  
  313.     cmtend ();                /* clean up after child */
  314.     cmtset ();                /* set up terminal again */
  315.     cmcsb._cmwrp = autowrap_column;    /* put that back */
  316.     return status;
  317. }
  318.  
  319. /*
  320.  * mm_execute:
  321.  * fork and execvp using given arguments and wait for child to terminate.
  322.  * based on shell() command above.
  323.  */
  324.  
  325. mm_execute (file, argv)
  326. char *file;
  327. char **argv;
  328. {
  329.     int pid, status;
  330.  
  331.     cmtend ();
  332.     if (cf)                /* update, since who knows what may */
  333.     update(&cf,0);            /* happen, and we turn of sig hndlrs */
  334.  
  335.     fix_signals_for_fork (true);
  336.  
  337.     pid = vfork ();
  338.     if (pid == 0) {
  339.     umask(OLD_UMASK);
  340.     execvp (file, argv);
  341.     perror (file);
  342.     _exit (1);
  343.     }
  344.     if (pid > 0)
  345.     status = wait_for_process (pid);
  346.     else
  347.     perror ("mm: fork");
  348.  
  349.     fix_signals_for_fork (false);
  350.  
  351.     cmtend ();                /* clean up after child */
  352.     cmtset ();                /* set up terminal again */
  353.     cmcsb._cmwrp = autowrap_column;    /* put that back */
  354.  
  355.     return status;
  356. }
  357.  
  358.  
  359. /* fancy exit */
  360. done(n)
  361. int n;
  362. {
  363.     cmtend ();
  364. #ifdef MDEBUG
  365.     m_done();
  366. #endif
  367. #ifdef USAGEFILE
  368.     usage_stop(USAGEFILE);
  369. #endif
  370.     exit (n);
  371. }
  372.  
  373.  
  374. /*
  375.  * CMD_SUSPEND:
  376.  * suspend MM.
  377.  */
  378.  
  379. cmd_suspend (n) 
  380. int n; 
  381. {
  382.   confirm();
  383.   suspend(0);
  384. }
  385.  
  386. /*
  387.  * suspend ourself.  this routine may be called either interactively,
  388.  * or from the SIGTSTP handler.
  389.  */
  390.  
  391. suspend (updflags)
  392. int updflags;                /* flags for update */
  393. {
  394. #ifdef SIGTSTP
  395.     long mask = block_signals ();
  396.  
  397.     cmtend ();                /* turn off ccmd's control of tty */
  398.  
  399.     if (cf)
  400.     update (&cf, updflags);        /* make sure file is saved */
  401.  
  402. #ifdef USAGEFILE
  403.     usage_stop (USAGEFILE);
  404. #endif
  405.  
  406.     (void) kill (0, SIGSTOP);        /* stop ourself */
  407.  
  408. #ifdef USAGEFILE
  409.     usage_start();
  410. #endif
  411.  
  412.     cmtend ();                /* XXX fix up terminal again */
  413.     cmtset ();                /* fix up the terminal */
  414.     cmcsb._cmwrp = autowrap_column;    /* cmtset resets this */
  415.  
  416.     release_signals (mask);
  417.  
  418. #else    /* !SIGTSTP */
  419.     printf ("The SUSPEND command is not supported on this system.\n");
  420. #endif    /* SIGTSTP */  
  421. }
  422.  
  423. signalhandler
  424. sighup_handler()
  425. {
  426.     if (cf != NULL)
  427.     update (&cf, UPD_SAVEMOD);    /* make sure file is saved */
  428.     done (1);
  429. }
  430.  
  431. #ifdef SIGXCPU
  432. signalhandler
  433. sigxcpu_handler() {
  434.     fprintf (stderr, "\
  435. WARNING: This process has received an XCPU signal!\n\
  436. This MM process has exceeded the per process CPU limit and will soon be\n\
  437. killed.  Please save your work and exit MM.\n");
  438.   if (cf != NULL) {
  439.       printf ("Saving %s ...\n", cf->filename);
  440.       update (&cf, UPD_SAVEMOD);
  441.   }
  442. }
  443. #endif /* SIGXCPU */
  444.  
  445. /*
  446.  * askem:
  447.  * ask them the question and encourage them to give us a good answer
  448.  */
  449.  
  450. #define MAXEOF 10
  451.  
  452. askem (tty, pr, prlen)
  453. int tty;
  454. char *pr;
  455. int prlen;
  456. {
  457.     char ans[BUFSIZ];
  458.     int i;
  459.     static char hint[] = "Please answer 'y' or 'n'\n";
  460.     int eofcount = 0;
  461.  
  462.     while (TRUE) {
  463.     i = 0;
  464.     write (tty, pr, prlen);
  465.     do {                /* get a line */
  466.         if (read (tty, &ans[i], 1) <= 0) {
  467.         eofcount++;
  468.         }
  469.         if (eofcount > MAXEOF)
  470.         return (TRUE);        /* assume yes */
  471.     } while ((ans[i++] != '\n') && (i < BUFSIZ));
  472.     if (i < BUFSIZ)
  473.         ans[i] = '\0';
  474.     if (i == 2) {
  475.         if ((ans[0] == 'n') || (ans[0] == 'N'))
  476.         return (FALSE);
  477.         if ((ans[0] == 'y') || (ans[0] == 'Y'))
  478.         return (TRUE);
  479.     }
  480.     if (ustrcmp (ans, "no\n") == 0)
  481.         return (FALSE);
  482.     if (ustrcmp (ans, "yes\n") == 0)
  483.         return (TRUE);
  484.  
  485.     /* failed, try again */
  486.     write (tty, hint, sizeof (hint)-1);
  487.     }
  488. }
  489.  
  490.  
  491. /*
  492.  * sigint_handler:
  493.  * handle sigint (^C) by saving the file and then exiting
  494.  * ask them if they really want to exit
  495.  */
  496. signalhandler
  497. sigint_handler()
  498. {
  499.     int tty;
  500.     static char ask[] = "\nDo you really want to exit MM? [y/n] ";
  501.     static char ask2[] = "\nSave mail file before exiting? [y/n] ";
  502.     static char warn[] = "Cannot save file, not exiting.\n\
  503. Try suspending MM, and making space by deleting some files.\n";
  504.     static char cont[] = "Continuing...\n";
  505.     char ans[3];
  506.     
  507.     cmtend ();                /* restore sane tty settings */
  508.     tty = open ("/dev/tty", O_RDWR, 0);    /* read and write at them */
  509.     if (tty >= 0 && !askem(tty, ask, sizeof (ask)-1)) {
  510.     write (tty, cont, sizeof(cont)-1);
  511.     close (tty);
  512.     cmtset ();            /* give tty back to CCMD */
  513.     return;                /* that's it, no exit */
  514.     }
  515.  
  516.     if ((cf != NULL) &&
  517.     (cf->flags & (MF_DIRTY|MF_MODIFIED))) { /* save the file */
  518.     if (tty < 0 || askem (tty, ask2, sizeof (ask2)-1)) {
  519.         if (!update (&cf, UPD_SAVEMOD|UPD_QUIET) 
  520.         && (cf->flags & MF_WRITERR)) {
  521.         if (tty >= 0) {
  522.             write (tty, warn, sizeof(warn)-1);
  523.             close (tty);
  524.             cmtset ();        /* give tty back to CCMD */
  525.             return;        /* keep going */
  526.         }
  527.         }
  528.     }
  529.     }
  530. #ifdef USAGEFILE
  531.     usage_stop(USAGEFILE);
  532. #endif
  533.     signal (SIGINT, SIG_DFL);
  534.     kill (PID, SIGINT);
  535. }
  536.  
  537. int
  538. wait_for_process (pid)
  539. int pid;
  540. {
  541.     return do_wait (pid, true);
  542. }
  543.  
  544. int
  545. collect_process (pid)
  546. int pid;
  547. {
  548.     return do_wait (pid, false);
  549. }
  550.  
  551. /*
  552.  * wait for a process, optionally blocking
  553.  *
  554.  * a pid of zero means we are being invoked by the
  555.  * SIGCHLD handler to collect the process that just
  556.  * exited.
  557.  *
  558.  * signal handlers should not set "blocking" to true.
  559.  */
  560.  
  561. int
  562. do_wait (pid, blocking)
  563. int pid, blocking;
  564. {
  565.     int n;
  566.     unsigned int status;
  567.  
  568.     if (blocking)
  569.     fix_signals_for_wait (true);
  570.  
  571.     do {
  572. #ifdef HAVE_WAIT3
  573.     union wait w;
  574.     int flags = blocking ? WUNTRACED : (WUNTRACED|WNOHANG);
  575. #endif
  576.     /*
  577.      * don't wait for a process that's already gone.
  578.      * this could happen if we're called while the
  579.      * SIGCHLD handler is active.
  580.      */
  581.     if (blocking && pid > 0 && kill (pid, 0) < 0 && errno == ESRCH) {
  582.         status = -1;
  583.         break;
  584.     }
  585.  
  586. #ifdef HAVE_WAIT3
  587.     n = wait3 (&w, flags, 0);
  588. #ifdef SIGCONT /* AIX 2.2.1 has wait3() but not SIGCONT */
  589.     if (n > 0 && w.w_stopval == WSTOPPED) {
  590.         (void) kill (n, SIGCONT);
  591.         continue;
  592.     }
  593. #endif
  594.     status = w.w_status;
  595. #else
  596.     n = wait (&status);
  597. #endif
  598.     if (n > 0)
  599.         forget_pid (n);
  600.     else
  601.         if (n == 0 && !blocking) {
  602.         status = 0;
  603.         break;
  604.         }
  605.         else
  606.         if (n < 0 && errno != EINTR) {
  607.             status = -1;
  608.             break;
  609.         }
  610.  
  611.     if (n == pid)
  612.         break;
  613.  
  614.     } while (blocking);
  615.  
  616.     if (blocking)
  617.     fix_signals_for_wait (false);
  618.  
  619.     return status;
  620. }
  621.  
  622. #define MAXBG 10
  623. static nrun = 0;
  624. static unsigned bg_procs[MAXBG];
  625.  
  626. maybe_wait_for_process (pid)
  627. int pid;
  628. {
  629.     int i;
  630.  
  631.     if (nrun < MAXBG) {
  632.     for (i = 0; i < MAXBG; i++)
  633.         if (bg_procs[i] == 0) {
  634.         bg_procs[i] = pid;
  635.         nrun++;
  636.         return;
  637.         }
  638.     }
  639.     /* see if any of the procs went away when we weren't looking */
  640.     for (i = 0; i < MAXBG; i++)
  641.     if ((kill (bg_procs[i], 0) < 0) && (errno == ESRCH)) {
  642.         bg_procs[i] = pid;        /* oh good, this slot's empty */
  643.         return;
  644.     }
  645.     
  646.     /*
  647.      * we've exceeded the number of allowed background processes,
  648.      * so we'll just have to wait for this one.
  649.      */
  650.     wait_for_process (pid);
  651. }
  652.  
  653. forget_pid (n)
  654. unsigned n;
  655. {
  656.     int i;
  657.     for (i = 0; i < MAXBG; i++)
  658.     if (bg_procs[i] == n) {
  659.         bg_procs[i] = 0;
  660.         --nrun;
  661.     }
  662. }
  663.